﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CSharp.Infrastructure
{
    public class ModelHelper
    {
        /// <summary>
        /// 直接转换 按照属性名称 匹配转换
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="t2"></param>
        /// <param name="isgore_ex">是否忽略异常 默认 true</param>
        /// <returns></returns>
        public static T1 ToOther<T1, T2>(T2 t2, bool isgore_ex = true) where T1 : class, new() where T2 : class, new()
        {
            T1 ret = new T1();
            var ps1 = typeof(T1).GetProperties().ToList();
            var ps2 = typeof(T2).GetProperties();
            foreach (var p in ps2)
            {
                var obj = p.GetValue(t2);
                var p1 = ps1.Find(a => a.Name == p.Name);
                if (p1 != null)
                {
                    try
                    {
                        p1.SetValue(ret, obj);
                    }
                    catch (Exception ex)
                    {
                        if (!isgore_ex)
                        {
                            throw ex;
                        }
                    }
                }
            }

            return ret;
        }

        /// <summary>
        /// 直接转换 按照属性名称 匹配转换
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="t2"></param>
        /// <param name="isgore_ex">是否忽略异常 默认 true</param>
        /// <returns></returns>
        public static List<T1> ToOtherList<T1, T2>(List<T2> t2_list, bool isgore_ex = true) where T1 : class, new() where T2 : class, new()
        {
            List<T1> ret = new List<T1>();
            foreach (var item in t2_list)
            {
                ret.Add(ToOther<T1, T2>(item, isgore_ex));
            }

            return ret;
        }

        /// <summary>
        /// 集合转表
        ///  Desciton + Table Attribute用于判定表的列明和表的名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="tb_name"></param>
        /// <param name="use_desc">是否使用Desc作为列明</param>
        /// <returns></returns>
        public static DataTable ListToTable<T>(List<T> list, string tb_name = "", bool use_desc = false) where T : class, new()
        {
            tb_name = ReadTbName<T>(tb_name);
            DataTable dt = new DataTable(tb_name);
            List<Tuple<string, string, PropertyInfo>> desc_list = new List<Tuple<string, string, PropertyInfo>>();
            var ps = typeof(T).GetProperties();
            foreach (var p in ps)
            {
                string desc = string.Empty;
                if (use_desc)
                {
                    var attributes = (DescriptionAttribute[])p.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    if (attributes.Length == 0)
                    {
                        continue;
                    }

                    desc = attributes[0].Description;
                    dt.Columns.Add(desc);
                }
                else
                {
                    dt.Columns.Add(p.Name);
                }

                desc_list.Add(Tuple.Create(p.Name, desc, p));
            }

            foreach (var item in list)
            {
                var dr = dt.NewRow();
                foreach (var key in desc_list)
                {
                    if (use_desc)
                    {
                        if (key.Item3.PropertyType == typeof(double))
                        {
                            try
                            {
                                dr[key.Item2] = Convert.ToDouble(key.Item3.GetValue(item, null));
                            }
                            catch
                            {
                                dr[key.Item2] = 0.0;
                            }
                        }
                        else if (key.Item3.PropertyType == typeof(int))
                        {
                            try
                            {
                                dr[key.Item2] = Convert.ToInt32(key.Item3.GetValue(item, null));
                            }
                            catch
                            {
                                dr[key.Item2] = 0;
                            }
                        }
                        else
                        {
                            dr[key.Item2] = key.Item3.GetValue(item, null) as string;
                        }
                    }
                    else
                    {
                        if (key.Item3.PropertyType == typeof(double))
                        {
                            try
                            {
                                dr[key.Item1] = Convert.ToDouble(key.Item3.GetValue(item, null));
                            }
                            catch
                            {
                                dr[key.Item1] = 0.0;
                            }
                        }
                        else if (key.Item3.PropertyType == typeof(int))
                        {
                            try
                            {
                                dr[key.Item1] = Convert.ToInt32(key.Item3.GetValue(item, null));
                            }
                            catch
                            {
                                dr[key.Item1] = 0;
                            }
                        }
                        else
                        {
                            dr[key.Item1] = key.Item3.GetValue(item, null) as string;
                        }
                    }
                }

                dt.Rows.Add(dr);
            }

            return dt;
        }

        /// <summary>
        /// 表转List
        /// Desciton + Table Attribute用于判定表的列明和表的名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static List<T> TableToList<T>(DataTable dt) where T : class, new()
        {
            List<T> result = new List<T>();
            var ps = typeof(T).GetProperties().ToList();

            // 属性名称 备注 属性对象
            List<Tuple<string, string, PropertyInfo>> desc_list = new List<Tuple<string, string, PropertyInfo>>();
            foreach (PropertyInfo p in ps)
            {
                var attributes = (DescriptionAttribute[])p.GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (attributes.Length == 0)
                {
                    continue;
                }

                string desc = attributes[0].Description;
                desc_list.Add(Tuple.Create(p.Name, desc, p));
            }

            Dictionary<string, PropertyInfo> col_list = new Dictionary<string, PropertyInfo>();
            foreach (DataColumn dc in dt.Columns)
            {
                var col = desc_list.Find(a => a.Item2 == dc.ColumnName);
                if (col != null)
                {
                    col_list.Add(col.Item1, col.Item3);
                }
            }

            foreach (DataRow dr in dt.Rows)
            {
                T t = new T();
                foreach (var col in col_list)
                {
                    col.Value.SetValue(t, dr[col.Key]);
                }

                result.Add(t);
            }

            return result;
        }

        private static string ReadTbName<T>(string tb_name) where T : class, new()
        {
            if (string.IsNullOrEmpty(tb_name))
            {
                System.ComponentModel.DataAnnotations.Schema.TableAttribute info = (System.ComponentModel.DataAnnotations.Schema.TableAttribute)typeof(T).GetCustomAttributes().First(a => a.GetType() == typeof(System.ComponentModel.DataAnnotations.Schema.TableAttribute));
                if (info == null)
                {
                    return nameof(T);
                }

                tb_name = info.Name;
            }

            return tb_name;
        }
    }
}
